package draw_by_gocui

import (
	"github.com/jroimartin/gocui"
	"go.uber.org/zap"
	"fmt"
	"sync"
	"gitee.com/lwow2025/snake-go/game"
	"gitee.com/lwow2025/snake-go/common/log"
	"gitee.com/lwow2025/snake-go/config"
)

func NewSnakeUI(quitFunc quitFunc) *SnakeUI {
	g, err := gocui.NewGui(gocui.Output256)
	if err != nil {
		panic("new gui failed:" + err.Error())
	}

	return &SnakeUI{
		gui: g,
		quitFunc: quitFunc,
	}
}

type playerActionPerformer interface {
	DoSnakeAction(action game.SnakeAction)
}

type quitFunc func()

type SnakeUI struct {
	gui *gocui.Gui
	gameView *gocui.View

	performer playerActionPerformer
	quitFunc quitFunc

	lock sync.Mutex
}

func (ui *SnakeUI) SetPerformer(performer playerActionPerformer) {
	ui.performer = performer
}

//func (ui *SnakeUI) SetQuitFunc(quitFunc quitFunc) {
//	ui.quitFunc = quitFunc
//}

func (ui *SnakeUI) Start() error {
	ui.gui.SetManagerFunc(ui.layout)
	ui.setKeyBindings()

	go func() {
		if err := ui.gui.MainLoop(); err != nil && err != gocui.ErrQuit {
			log.L.Error("main loop failed", zap.Error(err))
			ui.quitFunc()
			ui.Stop()
		}
	}()

	return nil
}

func (ui *SnakeUI) Stop() {
	ui.gui.Close()
}

// TODO: use size outside
func (ui *SnakeUI) Width() int {
	x, _ := ui.gui.Size()
	return x
}

func (ui *SnakeUI) Height() int {
	_, y := ui.gui.Size()
	return y
}

func (ui *SnakeUI) Output(ps []*game.Point)  {
	ui.lock.Lock()
	defer ui.lock.Unlock()

	if ui.gameView == nil {
		log.L.Debug("got out put, but view didn't prepared")
		return
	}

	ui.gameView.Clear()

	x, y := ui.gui.Size()
	f := MakeAFrame(x, y, ps)

	ui.gui.Update(func(gui *gocui.Gui) error {
		for _, row := range f {
			fmt.Fprintln(ui.gameView, row)
		}
		return nil
	})
}

func (ui *SnakeUI) layout(g *gocui.Gui) (err error) {
	ui.lock.Lock()
	defer ui.lock.Unlock()

	if ui.gameView != nil {
		return
	}

	maxX, maxY := g.Size()
	if ui.gameView, err = g.SetView("hello", 0, 0, maxX - 1, maxY - 1); err != nil {
		if err != gocui.ErrUnknownView {
			log.L.Warn("set view failed", zap.Error(err))
			return err
		}
	}
	ui.gameView.Title = "贪吃蛇"
	ui.gameView.Autoscroll = true
	ui.gameView.SelBgColor = gocui.ColorBlack

	return nil
}

func (ui *SnakeUI) setKeyBindings() error {
	g := ui.gui

	// quit
	if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, ui.quit); err != nil {
		return err
	}

	// p1
	if err := g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, ui.p1Up); err != nil {
		return err
	}
	if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, ui.p1Down); err != nil {
		return err
	}
	if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone, ui.p1Left); err != nil {
		return err
	}
	if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, ui.p1Right); err != nil {
		return err
	}

	// p2 can't support
	//if err := g.SetKeybinding("", gocui.KeyCtrlW, gocui.ModNone, ui.p2Up); err != nil {
	//	return err
	//}
	//if err := g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, ui.p1Down); err != nil {
	//	return err
	//}
	//if err := g.SetKeybinding("", gocui.KeyArrowLeft, gocui.ModNone, ui.p1Left); err != nil {
	//	return err
	//}
	//if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, ui.p1Right); err != nil {
	//	return err
	//}

	return nil
}
func (ui *SnakeUI) p1Up(_ *gocui.Gui, _ *gocui.View) error {
	return ui.p1Perform(game.Up)
}
func (ui *SnakeUI) p1Down(_ *gocui.Gui, _ *gocui.View) error {
	return ui.p1Perform(game.Down)
}
func (ui *SnakeUI) p1Left(_ *gocui.Gui, _ *gocui.View) error {
	return ui.p1Perform(game.Left)
}
func (ui *SnakeUI) p1Right(_ *gocui.Gui, _ *gocui.View) error {
	return ui.p1Perform(game.Right)
}

func (ui *SnakeUI) p2Up(_ *gocui.Gui, _ *gocui.View) error {
	return ui.p2Perform(game.Up)
}
func (ui *SnakeUI) p2Down(_ *gocui.Gui, _ *gocui.View) error {
	return ui.p2Perform(game.Down)
}
func (ui *SnakeUI) p2Left(_ *gocui.Gui, _ *gocui.View) error {
	return ui.p2Perform(game.Left)
}
func (ui *SnakeUI) p2Right(_ *gocui.Gui, _ *gocui.View) error {
	return ui.p2Perform(game.Right)
}

func (ui *SnakeUI) p1Perform(action game.Direction) error {
	ui.performer.DoSnakeAction(game.NewSnakeAction(config.Player1, action))
	return nil
}
func (ui *SnakeUI) p2Perform(action game.Direction) error {
	ui.performer.DoSnakeAction(game.NewSnakeAction(config.Player2, action))
	return nil
}

func (ui *SnakeUI) quit(_ *gocui.Gui, _ *gocui.View) error {
	if ui.quitFunc != nil {
		ui.quitFunc()
	}
	return gocui.ErrQuit
}